using Project.Configs;
using Project.Models;
using Project.Utilities;
using Project.Utilities.Abstractions;
using Project.ViewModels.Abstractions;
using UnityEngine;

namespace Project.ViewModels
{
    public class CharacterStatsPanelViewModel : DisposableViewModelBase
    {
        private readonly ReactiveProperty<string> _name;
        private readonly ReactiveProperty<Sprite> _sprite;

        private readonly ReactiveProperty<bool> _canIncreaseStrengthPoints;
        private readonly ReactiveProperty<bool> _canIncreaseAgilityPoints;
        private readonly ReactiveProperty<bool> _canIncreaseIntelligencePoints;

        private readonly ReactiveProperty<bool> _canDecreaseStrengthPoints;
        private readonly ReactiveProperty<bool> _canDecreaseAgilityPoints;
        private readonly ReactiveProperty<bool> _canDecreaseIntelligencePoints;

        private  Character _character;
        private  CharacterConfig _characterConfig;

        public CharacterStatsPanelViewModel(Character character, CharacterConfig characterConfig)
        {
            _character = character;
            _characterConfig = characterConfig;

            _name = new ReactiveProperty<string>();
            _sprite = new ReactiveProperty<Sprite>();

            _canIncreaseStrengthPoints = new ReactiveProperty<bool>();
            _canIncreaseAgilityPoints = new ReactiveProperty<bool>();
            _canIncreaseIntelligencePoints = new ReactiveProperty<bool>();

            _canDecreaseStrengthPoints = new ReactiveProperty<bool>();
            _canDecreaseAgilityPoints = new ReactiveProperty<bool>();
            _canDecreaseIntelligencePoints = new ReactiveProperty<bool>();

            _name.Value = _character.Name;
            _sprite.Value = _characterConfig.Sprite;

            OnPointsToSpendValueChanged(_character.PointsToSpend.Value);

            Subscribe();
        }

        public IReadOnlyReactiveProperty<string> Name => _name;
        public IReadOnlyReactiveProperty<Sprite> Sprite => _sprite;

        public IReadOnlyReactiveProperty<int> PointsToSpend => _character.PointsToSpend;

        public IReadOnlyPrimaryStatPoints StrengthPoints => _character.StrengthPoints;
        public IReadOnlyPrimaryStatPoints AgilityPoints => _character.AgilityPoints;
        public IReadOnlyPrimaryStatPoints IntelligencePoints => _character.IntelligencePoints;

        public IReadOnlySecondaryStatPoints HealthPoints => _character.HealthPoints;
        public IReadOnlySecondaryStatPoints ArmorPoints => _character.ArmorPoints;
        public IReadOnlySecondaryStatPoints ManaPoints => _character.ManaPoints;

        public IReadOnlyReactiveProperty<bool> CanIncreaseStrengthPoints => _canIncreaseStrengthPoints;
        public IReadOnlyReactiveProperty<bool> CanIncreaseAgilityPoints => _canIncreaseAgilityPoints;
        public IReadOnlyReactiveProperty<bool> CanIncreaseIntelligencePoints => _canIncreaseIntelligencePoints;

        public IReadOnlyReactiveProperty<bool> CanDecreaseStrengthPoints => _canDecreaseStrengthPoints;
        public IReadOnlyReactiveProperty<bool> CanDecreaseAgilityPoints => _canDecreaseAgilityPoints;
        public IReadOnlyReactiveProperty<bool> CanDecreaseIntelligencePoints => _canDecreaseIntelligencePoints;

        protected override void Subscribe()
        {
            PointsToSpend.ValueChanged += OnPointsToSpendValueChanged;

            StrengthPoints.ValueChanged += OnStrengthPointsValueChanged;
            AgilityPoints.ValueChanged += OnAgilityPointsValueChanged;
            IntelligencePoints.ValueChanged += OnIntelligencePointsValueChanged;
        }
        protected override void Unsubscribe()
        {
            PointsToSpend.ValueChanged -= OnPointsToSpendValueChanged;

            StrengthPoints.ValueChanged -= OnStrengthPointsValueChanged;
            AgilityPoints.ValueChanged -= OnAgilityPointsValueChanged;
            IntelligencePoints.ValueChanged -= OnIntelligencePointsValueChanged;
        }

        public void IncreaseStrengthPoints() => 
            ChangePrimaryStatPointsValue(_character.StrengthPoints, 1);
        public void IncreaseAgilityPoints() => 
            ChangePrimaryStatPointsValue(_character.AgilityPoints, 1);
        public void IncreaseIntelligencePoints() => 
            ChangePrimaryStatPointsValue(_character.IntelligencePoints, 1);

        public void DecreaseStrengthPoints() => 
            ChangePrimaryStatPointsValue(_character.StrengthPoints, -1);
        public void DecreaseAgilityPoints() => 
            ChangePrimaryStatPointsValue(_character.AgilityPoints, -1);
        public void DecreaseIntelligencePoints() => 
            ChangePrimaryStatPointsValue(_character.IntelligencePoints, -1);

        private void OnPointsToSpendValueChanged(int newValue)
        {
            OnStrengthPointsValueChanged(StrengthPoints.Value);
            OnAgilityPointsValueChanged(AgilityPoints.Value);
            OnIntelligencePointsValueChanged(IntelligencePoints.Value);
        }

        private void OnStrengthPointsValueChanged(int newValue) => 
            RefreshCanIncreaseAndCanDecreasePrimaryStatPoints(
                _canIncreaseStrengthPoints, 
                _canDecreaseStrengthPoints,
                StrengthPoints);
        private void OnAgilityPointsValueChanged(int newValue) => 
            RefreshCanIncreaseAndCanDecreasePrimaryStatPoints(
                _canIncreaseAgilityPoints,
                _canDecreaseAgilityPoints,
                AgilityPoints);
        private void OnIntelligencePointsValueChanged(int newValue) => 
            RefreshCanIncreaseAndCanDecreasePrimaryStatPoints(
                _canIncreaseIntelligencePoints,
                _canDecreaseIntelligencePoints,
                IntelligencePoints);

        private void RefreshCanIncreaseAndCanDecreasePrimaryStatPoints(
            ReactiveProperty<bool> canIncreasePrimaryStatPoints,
            ReactiveProperty<bool> canDecreasePrimaryStatPoints,
            IReadOnlyPrimaryStatPoints primaryStatPoints)
        {
            canIncreasePrimaryStatPoints.Value = 
                PointsToSpend.Value > 0 && 
                primaryStatPoints.Value < primaryStatPoints.MaxValue;
            canDecreasePrimaryStatPoints.Value =
                PointsToSpend.Value < _characterConfig.MaxPointsToSpendValue &&
                primaryStatPoints.Value > primaryStatPoints.MinValue;
        }

        private void ChangePrimaryStatPointsValue(PrimaryStatPoints primaryStatPoints, int value)
        {
            _character.PointsToSpend.Value -= value;
            primaryStatPoints.Value += value;
        }
    }
}
